 /*******************************************************************************
  * Copyright (c) 2006 Cognos Incorporated, IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  *******************************************************************************/
 package org.eclipse.osgi.framework.internal.protocol;

 import java.lang.reflect.Method ;
 import java.util.*;
 import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
 import org.eclipse.osgi.framework.log.FrameworkLogEntry;
 import org.osgi.framework.BundleContext;
 import org.osgi.service.packageadmin.PackageAdmin;
 import org.osgi.util.tracker.ServiceTracker;

 /*
  * An abstract class for handler factory impls (Stream and Content) that can
  * handle environments running multiple osgi frameworks with the same VM.
  */
 public abstract class MultiplexingFactory {

     protected static final String PACKAGEADMINCLASS = "org.osgi.service.packageadmin.PackageAdmin"; //$NON-NLS-1$
 protected BundleContext context;
     protected FrameworkAdaptor adaptor;
     private List factories; // list of multiplexed factories
 private ServiceTracker packageAdminTracker;

     // used to get access to the protected SecurityManager#getClassContext method
 private static class InternalSecurityManager extends SecurityManager {
         public Class [] getClassContext() {
             return super.getClassContext();
         }
     }

     private static InternalSecurityManager internalSecurityManager = new InternalSecurityManager();

     MultiplexingFactory(BundleContext context, FrameworkAdaptor adaptor) {
         this.context = context;
         this.adaptor = adaptor;
         packageAdminTracker = new ServiceTracker(context, PACKAGEADMINCLASS, null);
         packageAdminTracker.open();
     }

     abstract public void setParentFactory(Object parentFactory);

     abstract public Object getParentFactory();

     public synchronized boolean isMultiplexing() {
         return factories != null;
     }

     public synchronized void register(Object factory) {
         if (factories == null)
             factories = new LinkedList();

         // set parent for each factory so they can do proper delegation
 try {
             Class clazz = factory.getClass();
             Method setParentFactory = clazz.getMethod("setParentFactory", new Class [] {Object .class}); //$NON-NLS-1$
 setParentFactory.invoke(factory, new Object [] {getParentFactory()});
         } catch (Exception e) {
             adaptor.getFrameworkLog().log(new FrameworkLogEntry(MultiplexingFactory.class.getName(), FrameworkLogEntry.ERROR, 0, "register", FrameworkLogEntry.ERROR, e, null)); //$NON-NLS-1$
 throw new RuntimeException (e.getMessage());
         }
         factories.add(factory);
     }

     public synchronized void unregister(Object factory) {
         factories.remove(factory);
         if (factories.isEmpty())
             factories = null;
         // close the service tracker
 try {
             // this is brittle; if class does not directly extend MultplexingFactory then this method will not exist, but we do not want a public method here
 Method closeTracker = factory.getClass().getSuperclass().getDeclaredMethod("closePackageAdminTracker", null); //$NON-NLS-1$
 closeTracker.setAccessible(true); // its a private method
 closeTracker.invoke(factory, null);
         } catch (Exception e) {
             adaptor.getFrameworkLog().log(new FrameworkLogEntry(MultiplexingFactory.class.getName(), FrameworkLogEntry.ERROR, 0, "unregister", FrameworkLogEntry.ERROR, e, null)); //$NON-NLS-1$
 throw new RuntimeException (e.getMessage());
         }
     }

     public synchronized Object designateSuccessor() {
         Object parentFactory = getParentFactory();
         if (factories == null || factories.isEmpty())
             return parentFactory;

         Object successor = factories.remove(0);
         try {
             Class clazz = successor.getClass();
             Method register = clazz.getMethod("register", new Class [] {Object .class}); //$NON-NLS-1$
 for (Iterator it = factories.iterator(); it.hasNext();) {
                 register.invoke(successor, new Object [] {it.next()});
             }
         } catch (Exception e) {
             adaptor.getFrameworkLog().log(new FrameworkLogEntry(MultiplexingFactory.class.getName(), FrameworkLogEntry.ERROR, 0, "designateSuccessor", FrameworkLogEntry.ERROR, e, null)); //$NON-NLS-1$
 throw new RuntimeException (e.getMessage());
         }
         factories = null;
         closePackageAdminTracker(); // close tracker
 return successor;
     }

     private void closePackageAdminTracker() {
         packageAdminTracker.close();
     }

     public synchronized Object findAuthorizedFactory(List ignoredClasses) {
         Class [] classStack = internalSecurityManager.getClassContext();
         for (int i = 0; i < classStack.length; i++) {
             Class clazz = classStack[i];
             if (clazz == InternalSecurityManager.class || clazz == MultiplexingFactory.class || ignoredClasses.contains(clazz))
                 continue;
             if (hasAuthority(clazz))
                 return this;
             if (factories == null)
                 continue;
             for (Iterator it = factories.iterator(); it.hasNext();) {
                 Object factory = it.next();
                 try {
                     Method hasAuthorityMethod = factory.getClass().getMethod("hasAuthority", new Class [] {Class .class}); //$NON-NLS-1$
 if (((Boolean ) hasAuthorityMethod.invoke(factory, new Object [] {clazz})).booleanValue()) {
                         return factory;
                     }
                 } catch (Exception e) {
                     adaptor.getFrameworkLog().log(new FrameworkLogEntry(MultiplexingFactory.class.getName(), FrameworkLogEntry.ERROR, 0, "findAuthorizedURLStreamHandler-loop", FrameworkLogEntry.ERROR, e, null)); //$NON-NLS-1$
 throw new RuntimeException (e.getMessage());
                 }
             }
         }
         return null;
     }

     public boolean hasAuthority(Class clazz) {
         PackageAdmin packageAdminService = (PackageAdmin) packageAdminTracker.getService();
         if (packageAdminService != null) {
             return packageAdminService.getBundle(clazz) != null;
         }
         return false;
     }
 }

